%!Tex Program = xelatex
%\documentclass[a4paper]{article}
\documentclass[a4paper]{ctexart}
\usepackage{xltxtra}
\usepackage{listings}

\lstdefinestyle{shell}{
    language=sh,
    basicstyle=\ttfamily,
    keywordstyle=\color{blue},
    commentstyle=\color{gray},
    backgroundcolor=\color{lightgray}
}

%\setmainfont[Mapping=tex-text]{AR PL UMing CN:style=Light}
%\setmainfont[Mapping=tex-text]{AR PL UKai CN:style=Book}
%\setmainfont[Mapping=tex-text]{WenQuanYi Zen Hei:style=Regular}
%\setmainfont[Mapping=tex-text]{WenQuanYi Zen Hei Sharp:style=Regular}
%\setmainfont[Mapping=tex-text]{AR PL KaitiM GB:style=Regular} 
%\setmainfont[Mapping=tex-text]{AR PL SungtiL GB:style=Regular} 
%\setmainfont[Mapping=tex-text]{WenQuanYi Zen Hei Mono:style=Regular} 


\title{第五讲：Linux 编程工具}
\author{王何宇}
\date{}

\begin{document}
\maketitle
\pagestyle{empty}

\section{make}

实际上我们已经知道了 make 和 Makefile 的基本用法和写法。如果你的项目有多个文件构成，
这些文件形成了交叉引用过程，make 可以高效地管理这些文件的编译过程。事实上，make
过程可以看作是一个特殊的 shell 脚本执行，而 Makefile (M 也可以小写) 就是这个脚本。

make 的编译过程遵循：

\begin{lstlisting}[language=sh]
target: dependencies
(a tab)rules  
\end{lstlisting}

配合上我们之前学过的 shell 编程，通配符，正则表达式，我们可以写出很复杂的逻辑过程。
这些复杂性主要体现在尽量让 make 过程具有一般性。

先来给我们之前的项目 hello.dy 做一个 Makefile

\begin{lstlisting}[language=sh]
all: hello
# Which compiler
CC = gcc
# Where are include files kept
INCLUDE = include
LIBS = lib
SOURCES = src
# Options for development
CFLAGS = -g -Wall 
# Options for release
# CFLAGS = -O3 -Wall

hello: main.o $(LIBS)/libhello.so 
	$(CC) -o hello main.o -L$(LIBS) -Wl,-rpath=$(LIBS) -lhello 

main.o: main.c $(INCLUDE)/hello.h
	$(CC) -c main.c -I$(INCLUDE)

$(LIBS)/libhello.so: $(SOURCES)/hello.c
	$(CC) -shared -o $(LIBS)/libhello.so $(SOURCES)/hello.c

clean:
	rm -f $(LIBS)/*.so hello *.o

.PHONY: all clean
\end{lstlisting}

这里注意：
\begin{itemize}
\item 变量赋值的时候，\verb|SOURCES = src| 后面不能有空格，否则引用的时候就会出错；
\item 编译选项 \verb|-Wl,-rpath=$(LIBS)| 是通过编译器将动态链接库位置记录下来，
  这样就不用改环境变量了。这也是一个常用的方法。
  (用一下gsl27:
\begin{lstlisting}[language=sh]
  gcc -o test27 interp.c -I/usr/local/gsl-2.7.1/include/ -L/usr/local/gsl-2.7.1/lib/ -Wl,-rpath=/usr/local/gsl-2.7.1/lib -lgsl -lm -lgslcblas
\end{lstlisting}
  )  
\item 这里 \verb|.PHONY: all clean| 称为伪目标，以防真的出现一个叫做 all 或者 clean
  的文件（当然我们要避免这一点）。
\end{itemize}

然后 Makefile 提供了一些自动化变量来增加通用性，比如：

\begin{itemize}
\item \verb|$@|，表示目标文件（target），即当前规则中的目标对象。比如：
\begin{lstlisting}[language=sh]
$(LIBS)/libhello.so: $(SOURCES)/hello.c
	$(CC) -shared -o $(LIBS)/libhello.so $(SOURCES)/hello.c
\end{lstlisting}
可以改为
\begin{lstlisting}[language=sh]
$(LIBS)/libhello.so: $(SOURCES)/hello.c
	$(CC) -shared -o $@ $(SOURCES)/hello.c
\end{lstlisting}

\item \verb|$<|，表示第一个依赖文件（dependency），即当前规则中的第一个依赖文件。
  比如
\begin{lstlisting}[language=sh]
$(LIBS)/libhello.so: $(SOURCES)/hello.c
	$(CC) -shared -o $(LIBS)/libhello.so $(SOURCES)/hello.c
\end{lstlisting}
可以改为
\begin{lstlisting}[language=sh]
$(LIBS)/libhello.so: $(SOURCES)/hello.c
	$(CC) -shared -o $@ $<
\end{lstlisting}
\item \verb|$?|，表示所有比目标更新的依赖文件列表，
  即当前规则中所有（因为用户或其他原因改动）需要重新构建的文件。
\item \verb|$*|，表示目标文件去除后缀的部分，即当前规则中的目标文件名（不包括扩展名）。
\item \verb|$^|，表示所有的依赖文件列表，即当前规则中的所有依赖文件。
  通常用于编译链接（二进制文件链接）命令中。
\item \verb|$+|，类似于 \verb|$^|，表示所有的依赖文件列表，
  但是如果在依赖列表中出现重复的文件，它会保留所有重复的文件。
  通常用于编译命令中，当需要将重复的文件都传递给编译器时使用。
\item \verb|$(@D)|，表示目标文件所在的目录。通常用于在命令中创建目标所在的目录。
\item \verb|$(@F)|，表示目标文件的文件名部分。通常用于在命令中获取目标文件的文件名。
  比如我们可以将
\begin{lstlisting}[language=sh]
hello: main.o $(LIBS)/libhello.so 
	$(CC) -o hello main.o -L$(LIBS) -Wl,-rpath=$(LIBS) -lhello 
\end{lstlisting}
改为
\begin{lstlisting}[language=sh]
hello: main.o $(LIBS)/libhello.so 
	$(CC) -o $@ $< -L$(LIBS) -Wl,-rpath=$(LIBS) -l$(@F)
\end{lstlisting}
\item \verb|$(<D)|，表示依赖文件所在的目录。通常用于在命令中获取依赖文件所在的目录。
\item \verb|$(<F)|，表示依赖文件的文件名部分。通常用于在命令中获取依赖文件的文件名。
\end{itemize}

我们部分利用上述自动变量，更新一下我们的 Makefile，不但简洁了，更重要的是更智能了。
Makefile 中也可以使用通配符，但是和 shell 中有点区别，比如 \verb|%.c| 代表所有的
\verb|.c| 文件。如果我们需要把所有的 \verb|.c| 文件都先生成 \verb|.o| 文件，
我们就可以这样制定规则：
\begin{lstlisting}[language=sh]
%.o: %.c
	$(CC) -c $<
\end{lstlisting}
不但更简单，而且很通用。现在
\begin{lstlisting}[language=sh]
main.o: main.c $(INCLUDE)/hello.h
	$(CC) -c main.c -I$(INCLUDE)
\end{lstlisting}
进一步改为
\begin{lstlisting}[language=sh]
%.o: %.c
	$(CC) -c $< -I$(INCLUDE)
\end{lstlisting}
注意这句话几乎是万精油。

现在我们的 Makefile 也长得和天书一样了，但是通用性和确定性却好了很多。
注意 make 还可以套娃，也就是每个子目录下都存放一个 Makefile，所以有无穷的可能可以搭配。
以至于像 libgsl 那样的包需要用一套脚本专门来生成 Makefile。现在我们对 libgsl
生成的 Makefile 已经可以自己分析一下了，如果编译过程中遇到什么问题，也可以自己理解一下。
通常都是缺少一些必要的文件导致。

\section{Minicoda}

考虑了一下还是介绍一下如何在科学计算中使用 Python 吧。直接去 tuna 站下载 Minicoda。
按日期排序下载最新的 Linux 版:

\begin{lstlisting}[language=sh]
https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/Miniconda3-latest-Linux-x86_64.sh
\end{lstlisting}

注意看这竟然是一个 bash 的安装包，它头几行是 bash，其实是个二进制包。

但不管怎么样，直接运行就好。

\begin{lstlisting}[language=sh]
bash Miniconda3-latest-Linux-x86_64.sh
\end{lstlisting}

一路 yes 到底就行。接下去要赶紧换一个源，不然太慢。

\begin{lstlisting}[language=sh]
conda config --set show_channel_urls yes
\end{lstlisting}

然后把 tuna 的源加到文件 \verb|.condarc|：

\begin{lstlisting}[language=sh]
channels:
  - defaults
show_channel_urls: true
default_channels:
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/msys2
custom_channels:
  conda-forge: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  msys2: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  bioconda: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  menpo: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  pytorch: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  pytorch-lts: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  simpleitk: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  deepmodeling: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/
\end{lstlisting}

现在重启终端，就可以愉快地使用 python 了。注意此时提示符多了一个(base)，
相应的修改在.bashrc，miniconda明显修改了 \verb|$PS1|。

这个是 miniconda 的环境名。base 环境我们永远不要去修改它，为我们的工作建立一个新环境。

\begin{lstlisting}[language=sh]
conda create -n teaching numpy scipy matplotlib sympy jupyter 
\end{lstlisting}

如果你不是 intel 芯片的机器，最后一项可以不加。





\bibliographystyle{plain}
\bibliography{crazyfish.bib}

\end{document}
